package com.smart.web3j;

import java.io.IOException;
import java.math.BigInteger;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.crypto.CipherException;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthChainId;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.core.methods.response.Transaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.RawTransactionManager;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import com.smart.web3j.model.RawTransactionRequest;


@Service
public class Web3Service {

	@Autowired
	private Web3j web3j;

	public String web3ClientVersion() throws IOException {
		return this.web3j.web3ClientVersion().send().getWeb3ClientVersion();
	}
	
	public String getBalance(String address) throws IOException {
		EthGetBalance balance = this.web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
		return Convert.fromWei(balance.getBalance().toString(), Convert.Unit.ETHER).toPlainString();
	}

	public BigInteger getGasPrice() throws IOException {
		return this.web3j.ethGasPrice().send().getGasPrice();
	}
	
	public BigInteger getCurrentGasPrice(String privateKey) throws IOException {
		Credentials credentials = Credentials.create(privateKey);
		EthChainId ethChainId = this.web3j.ethChainId().send();
		TransactionManager transactionManager = new RawTransactionManager(this.web3j, credentials, ethChainId.getId());
		Transfer transfer = new Transfer(this.web3j, transactionManager);
		return transfer.requestCurrentGasPrice();
	}

	public BigInteger GetNonce(String address) throws IOException {
		EthGetTransactionCount ethGetTransactionCount = this.web3j.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).send();
		return ethGetTransactionCount.getTransactionCount();
	}
	
	public String rawTransaction(RawTransactionRequest request) throws IOException, CipherException {
		EthGetTransactionCount ethGetTransactionCount = this.web3j.ethGetTransactionCount(request.getFromAddress(), DefaultBlockParameterName.PENDING).send();
		BigInteger nonce = ethGetTransactionCount.getTransactionCount();
		BigInteger chainId = this.web3j.ethChainId().send().getChainId();
		BigInteger toEtherValue = Convert.toWei(request.getToEtherValue().toString(), Convert.Unit.ETHER).toBigInteger();
		RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, this.getGasPrice(), Transfer.GAS_LIMIT, request.getToAddress(), toEtherValue);
		Credentials credentials = Credentials.create(request.getPrivateKey());
		byte[] signature = TransactionEncoder.signMessage(rawTransaction, chainId.longValue(), credentials);
		String hexSignature = Numeric.toHexString(signature);
		EthSendTransaction ethSendTransaction = this.web3j.ethSendRawTransaction(hexSignature).send();
		return ethSendTransaction.getTransactionHash();
	}
	
	public Transaction transactionByHash(String txHash) throws IOException {
		EthTransaction ethTransaction = this.web3j.ethGetTransactionByHash(txHash).send();
		return ethTransaction.getResult();
	}

	public TransactionReceipt transactionReceipt(String txHash) throws IOException {
		EthGetTransactionReceipt ethGetTransactionReceipt = this.web3j.ethGetTransactionReceipt(txHash).send();
		return ethGetTransactionReceipt.getResult();
	}
	
	public EthBlock.Block getBlockByHash(String transactionHash) throws IOException {
		EthBlock ethBlock = this.web3j.ethGetBlockByHash(transactionHash, true).send();
		return ethBlock.getResult();
	}
}
